#pragma once
#include "component/GameComponent.h"
#include <bitset>
#include "Vector3.h"
#include <cmath>
#include "../util/Helper.h"



namespace Boon
{
	//fwds
	inline uint_32 Log2( uint_32 n );
	namespace Physics{ class IPhysics; }

	class BOON_API Rigidbody : public GameComponent
	{
		enum RigidSettings
		{
			RB_IS_KINEMATIC		= 0,
			RB_USE_GRAVITY		= 1,
			RB_DIRTY			= 2,
			COUNT				= 3
		};

		GAMECOMPONENT( Rigidbody, GameComponent )

	public:
		/*! \brief		Set if rigidbody uses physics default gravity
			\param		flag - Enable or disable
		*/
		inline void					SetUseGravity( bool flag ){ m_Flags.set( RB_USE_GRAVITY, flag ); SetDirty(); }

		/*! \brief		Returns if rigidbody is using default physics gravity
		*/
		inline bool					IsUsingGravity() const{ return m_Flags.at( RB_USE_GRAVITY ) == true; }

		/*! \brief		
			\param		mass - Sets the mass in kilograms (kg) of the rigidbody
		*/
		inline void					SetMass( float mass ) { if( mass >= 0 ) m_Mass = mass; SetDirty(); }

		/*! \brief		Returns the mass of the rigidbody
			\return		float - Returns the mass in kg
		*/
		inline float				GetMass() const { return m_Mass; }
		/*! \brief		Set this rigidbody to kinematic so that is will interact with other rigidbodies but will be immune to forces
			\param		flag - True or false
		*/
		inline void					SetKinematic( bool flag ) { m_Flags.set( RB_IS_KINEMATIC, flag ); SetDirty(); }

		/*! \brief		Return if this body is kinematic
			\return		bool - True if kinematic 
		*/
		inline bool					IsKinematic() const{ return m_Flags.at( RB_IS_KINEMATIC ) == true; }

		/*! \brief		Set constraints to the 6 degrees of freedom
			\param		constraint - use enum RigidbodyConstraint and concatenate with operator |
			\param		flag - True or false
		*/
		inline void					SetConstraints( uint_32 constraints, bool flag ) { m_Constraints &= ~(constraints); if( flag ) m_Constraints |= constraints; SetDirty(); }

		/*! \brief		Returns all constraints set to this rigidbody
			\return		Boon::RigidbodyConstraint
		*/
		inline RigidbodyConstraint	GetConstraints() const { return (RigidbodyConstraint)m_Constraints; }

		/*! \brief		Check if a single constraint is enabled on this rigidbody
			\param		constraint - Constraint to check
			\return		bool - Return true if constraint is set
		*/
		inline bool					IsConstrained( RigidbodyConstraint constraint ) const {	return ( m_Constraints & constraint ) == constraint; }

		/*! \brief		Adds a given force in world space to the rigidbody
			\param		force - Given force which will be applied
			\param		mode - Force mode
		*/
		void						AddForce( const Vector3& force, ForceMode mode );

		/*! \brief		Adds a given force in world space to the rigidbody
			\param		x - Force on world x axis
			\param		y - Force on world y axis
			\param		z - Force on world z axis
			\param		mode - Force mode
		*/
		void						AddForce( float x, float y, float z, ForceMode mode );

		/*! \brief		Sets the given velocity in world space to the rigidbody
			\param		velocity - Velocity given by a vector3
		*/
		void						SetVelocity( const Vector3& velocity );

		/*! \brief		Sets the given velocity in world space to the rigidbody
			\param		x - Velocity in world space on the x axis
			\param		y - Velocity in world space on the y axis 
			\param		z - Velocity in world space on the z axis 
		*/
		void						SetVelocity( float x, float y, float z );

		const Vector3				GetVelocity() const;


	private:
		virtual void				OnCreate() sealed;
		virtual void				OnEnable() sealed;
		virtual void				OnStart() sealed;
		virtual void				Update() sealed;
		virtual void				LateUpdate() sealed;
		virtual void				OnDisable() sealed;
		virtual void				OnDestroy() sealed;
		inline void					SetDirty(){ m_Flags.set( RB_DIRTY, true ); }

		Physics::IPhysics*			m_Physics;
		std::bitset<8>				m_Flags;
		unsigned char				m_Constraints;
		float						m_Mass;
		Vector3						m_Velocity;
	};

}
